package cn.wubo.dynamic.schedule.service;

import cn.wubo.dynamic.schedule.controller.TaskRecordVO;
import cn.wubo.dynamic.schedule.controller.TaskVO;
import cn.wubo.dynamic.schedule.core.DynamicScheduledTaskService;
import cn.wubo.dynamic.schedule.entity.TaskRecord;
import cn.wubo.dynamic.schedule.exception.BusinessRuntimeException;
import cn.wubo.loader.util.MethodUtils;
import cn.wubo.sql.util.*;
import com.alibaba.druid.DbType;
import groovy.lang.GroovyClassLoader;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class TaskService {

    @Resource
    DynamicScheduledTaskService dynamicScheduledTaskService;
    private static Map<String, String> taskStatus = new ConcurrentHashMap<>();

    static {
        MutilConnectionPool.init("test", "jdbc:h2:file:./data/demo;AUTO_SERVER=TRUE", "sa", "");
        //MutilConnectionPool.init("test", "jdbc:mysql://localhost:3306/dsd", "root", "123456");
        //MutilConnectionPool.init("test", "jdbc:postgresql://localhost:5432/postgres", "postgres", "Abc1234%");
    }

    /**
     * 分页查询任务记录
     *
     * @param taskVO 任务记录对象
     * @return 分页后的任务记录集合
     */
    public PageVO<TaskRecordVO> page(TaskVO taskVO) {
        // 创建 SQL 对象
        SQL<TaskRecord> sqlSelect = new SQL<TaskRecord>() {
        }.select("*").dialect(DbType.h2).page((taskVO.getPage() - 1) * taskVO.getLimit(), taskVO.getLimit());

        // 添加 WHERE 条件
        if (StringUtils.hasText(taskVO.getMemo())) sqlSelect.addWhereLIKE("memo", taskVO.getMemo());
        if (StringUtils.hasText(taskVO.getMethodName())) sqlSelect.addWhereLIKE("method_name", taskVO.getMethodName());
        if (StringUtils.hasText(taskVO.getGroovyContext()))
            sqlSelect.addWhereLIKE("groovy_context", taskVO.getGroovyContext());
        if (StringUtils.hasText(taskVO.getIsStart())) sqlSelect.addWhereEQ("is_start", taskVO.getIsStart());

        try (Connection conn = MutilConnectionPool.getConnection("test")) {
            // 执行 SQL 查询
            List<TaskRecordVO> page = sqlSelect.executeQuery(conn).stream().map(taskRecord -> new TaskRecordVO(taskRecord, taskStatus.getOrDefault(taskRecord.getId(), "0"))).toList();

            // 查询总数量
            Long count = (Long) ExecuteSqlUtils.executeQuery(conn, "select count(1) from task_record", new TypeReference<Map<String, Object>>() {
            }).get(0).entrySet().stream().findFirst().orElseThrow(() -> new BusinessRuntimeException("取总数量异常!")).getValue();

            // 返回分页结果
            return new PageVO<>(count, page);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取任务记录列表
     *
     * @return 任务记录列表
     */
    public List<TaskRecord> list() {
        // 使用多连接池执行查询操作
        return MutilConnectionPool.run("test", conn -> ModelSqlUtils.selectSql(new TaskRecord()).executeQuery(conn));
    }

    /**
     * 保存任务记录
     *
     * @param taskRecord 任务记录对象
     * @return 保存的记录数（成功保存的记录数为1，更新的记录数为1，新增的记录数为1）
     */
    public int save(TaskRecord taskRecord) {
        // 检查任务记录ID是否存在
        if (StringUtils.hasText(taskRecord.getId()))
            // 使用多连接池执行更新操作
            return MutilConnectionPool.run("test", conn -> ModelSqlUtils.updateSql(taskRecord).executeUpdate(conn));
        else
            // 使用多连接池执行插入操作
            return MutilConnectionPool.run("test", conn -> ModelSqlUtils.insertSql(taskRecord).executeUpdate(conn));
    }

    /**
     * 删除指定id的任务记录
     *
     * @param id 要删除的任务记录的id
     * @return 删除成功的操作结果
     */
    public int delete(String id) {
        // 如果任务状态为未完成，则先停止任务
        if (taskStatus.containsKey(id) && "1".equals(taskStatus.get(id))) stop(id);
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setId(id);
        // 使用多连接池执行删除操作
        return MutilConnectionPool.run("test", conn -> ModelSqlUtils.deleteSql(taskRecord).executeUpdate(conn));
    }

    /**
     * 根据任务ID获取任务记录
     *
     * @param id 任务ID
     * @return 任务记录对象
     * @throws BusinessRuntimeException 数据异常时抛出的异常
     */
    public TaskRecord get(String id) {
        TaskRecord taskRecord = new TaskRecord();
        taskRecord.setId(id);
        // 使用多连接池执行查询操作
        List<TaskRecord> list = MutilConnectionPool.run("test", conn -> ModelSqlUtils.selectSql(taskRecord).executeQuery(conn));
        // 如果查询结果不为空且只有一个元素，则返回该元素
        if (!CollectionUtils.isEmpty(list) && list.size() == 1) return list.get(0);
            // 如果查询结果为空或有多个元素，则抛出数据异常
        else throw new BusinessRuntimeException("数据异常");
    }

    /**
     * 启动指定ID的任务。
     * 此方法尝试启动一个任务，如果任务尚未启动或处于停止状态，则会将其添加到动态调度任务列表中并设置其状态为启动。
     * 如果任务已经启动，则不进行任何操作。
     *
     * @param id 任务的唯一标识符。
     * @return 总是返回{@code true}，表示方法执行成功。
     * @throws BusinessRuntimeException 如果启动任务过程中发生异常，则抛出此运行时异常。
     */
    public Boolean start(String id) {
        try {
            // 检查任务状态，如果任务不存在或处于停止状态，则尝试启动任务
            if (!taskStatus.containsKey(id) || taskStatus.get(id).equals("0")) {
                // 根据ID获取任务记录
                TaskRecord taskRecord = get(id);
                // 将任务添加到动态调度服务中，并设置相应的执行周期和执行逻辑
                dynamicScheduledTaskService.add(taskRecord.getId(), taskRecord.getCron(), new RunGroovyCode(taskRecord));
                // 更新任务状态为启动
                taskStatus.put(id, "1");
            }
            return Boolean.TRUE;
        } catch (Exception e) {
            // 如果在启动任务过程中发生异常，将任务状态设置为停止，并重新抛出一个业务运行时异常
            taskStatus.put(id, "0");
            throw new BusinessRuntimeException(e);
        }
    }

    /**
     * 停止任务
     *
     * @param id 任务ID
     * @return 是否成功停止任务
     */
    public Boolean stop(String id) {
        try {
            dynamicScheduledTaskService.cancel(id);
            taskStatus.put(id, "0");
            return Boolean.TRUE;
        } catch (Exception e) {
            throw new BusinessRuntimeException(e);
        }
    }

    /**
     * 执行一次任务。
     *
     * 本方法旨在通过提供的任务ID，检索任务记录，并使用GroovyClassLoader解析任务的Groovy脚本，
     * 随后调用脚本中指定的方法。如果解析或执行过程中出现IO异常，则抛出业务运行时异常。
     *
     * @param id 任务的唯一标识符。用于检索任务记录。
     * @return 始终返回Boolean.TRUE，表示任务执行成功（考虑到方法的逻辑并不涉及返回值的判断）。
     * @throws BusinessRuntimeException 如果解析Groovy类或调用方法过程中发生IO异常，则抛出此运行时异常。
     */
    public Boolean runOnce(String id) {
        // 根据任务ID获取任务记录
        TaskRecord taskRecord = get(id);
        try (GroovyClassLoader groovyClassLoader = new GroovyClassLoader()) {
            // 解析任务记录中的Groovy脚本，生成类
            Class<?> clazz = groovyClassLoader.parseClass(taskRecord.getGroovyContext());
            // 调用脚本中指定的方法
            MethodUtils.invokeClass(clazz, taskRecord.getMethodName());
            // 返回执行成功的标志
            return Boolean.TRUE;
        } catch (IOException e) {
            // 抛出业务运行时异常，封装IO异常
            throw new BusinessRuntimeException(e);
        }
    }

    /**
     * 检查表是否存在，如果不存在则创建表
     */
    public void checkTable() {
        TaskRecord taskRecord = new TaskRecord();
        if (Boolean.FALSE.equals(MutilConnectionPool.run("test", conn -> ModelSqlUtils.SQL(taskRecord).isTableExists(conn))))
            MutilConnectionPool.run("test", conn -> ModelSqlUtils.createSql(taskRecord).createTable(conn));
    }

}
